import copy
from datetime import datetime
import json
from flask import current_app
from decorators import Transactional
from enums.yes_no_enum import YesNoEnum
from mauth.login_user_holder import LoginUserHolder
from mauth.mtoken import TokenStrategyFactory
from mexception import AssertTool
from models.dept import Dept
from models.post import Post
from models.user import User
from packages.engine.enums.flow_const import FlowConst
from packages.engine.util.reflect_util import ReflectUtil
from services import BaseService
from models.user_role import UserRole
from services.enums.user_error_enum import UserErrorEnum
from tools import generate_id, md5_encrypt, random_string, recursion_tree
from tools.db_tool import DbTool
from flask_redis import FlaskRedis

redis_client = FlaskRedis()


class UserService(BaseService):
    """
    用户模块业务处理类
    """

    def page(self, form):
        # 重写分页查询，追加roleIds字段
        pageNum = form.data.get("pageNum", 1)
        pageSize = form.data.get("pageSize", 10)
        page = (
            DbTool.filter_by_custom(self.model)
            .filter(getattr(self.model, "adminType") != 1)
            .paginate(pageNum, pageSize, False)
        )
        res = self.model.to_page(page)
        for item in res.get("rows"):
            userRoles = UserRole.query.filter_by(userId=item["id"]).all()
            item["roleIds"] = ",".join([str(userRole.roleId) for userRole in userRoles])
        return res

    def info(self):
        """
        获取当前登录用户信息
        :return:
        """
        return LoginUserHolder.get()

    def perm_code(self):
        """
        获取当前登录用户权限码
        :return:
        """
        return LoginUserHolder.get().get("perms")

    @Transactional()
    def update_pwd(self, form):
        """
        修改用户个人密码
        :param form:
        :return:
        """
        password = form.get("password")
        newPassword = form.get("newPassword")
        if password == newPassword:
            AssertTool.raise_biz(UserErrorEnum.USER_PWD_REPEAT)
        userId = form.get("id")
        user = User.query.filter_by(id=userId).first()
        if user is None:
            AssertTool.raise_biz(UserErrorEnum.USER_NOT_EXIST)
        # 旧密码正常，才能修改
        encryptPwd = md5_encrypt(password + user.salt)
        if encryptPwd != user.password:
            AssertTool.raise_biz(UserErrorEnum.PWS_ERROR)
        salt = random_string(8)
        self.db.session.query(User).filter_by(id=userId).update(
            {"salt": salt, "password": md5_encrypt(newPassword + salt)}
        )

    @Transactional()
    def reset_pwd(self, userIds):
        """
        重置用户密码
        :param userIds:
        :return:
        """
        if userIds is None or len(userIds) == 0:
            return
        userList = (
            self.db.session.query(User)
            .filter(User.id.in_(userIds))
            .filter(User.adminType != 1)
            .all()
        )
        for user in userList:
            salt = random_string(8)
            default_password = current_app.config.get("M_DEFAULT_PASSWORD", "123456")
            encryptPwd = md5_encrypt(default_password + salt)
            self.db.session.query(User).filter_by(id=user.id).update(
                {"salt": salt, "password": encryptPwd}
            )

    @Transactional()
    def locked(self, userIds):
        """
        锁定用户
        :param userIds:
        :return:
        """
        if userIds is None or len(userIds) == 0:
            return
        userList = (
            self.db.session.query(User)
            .filter(User.id.in_(userIds))
            .filter(User.adminType != 1)
            .all()
        )
        for user in userList:
            self.db.session.query(User).filter_by(id=user.id).update(
                {
                    "isLocked": YesNoEnum.YES.code,
                }
            )

    @Transactional()
    def un_locked(self, userIds):
        """
        取消锁定用户
        :param userIds:
        :return:
        """
        if userIds is None or len(userIds) == 0:
            return
        userList = (
            self.db.session.query(User)
            .filter(User.id.in_(userIds))
            .filter(User.adminType != 1)
            .all()
        )
        for user in userList:
            self.db.session.query(User).filter_by(id=user.id).update(
                {
                    "isLocked": YesNoEnum.NO.code,
                }
            )

    @Transactional()
    def grant_role(self, userId, roleIds):
        """
        为用户授予角色
        :param userId:
        :param roleIds:
        :return:
        """
        # 删除用户所有角色关系
        self.db.session.query(UserRole).filter_by(userId=userId).delete()
        if roleIds is None or len(roleIds) == 0:
            return
        for roleId in roleIds:
            userRole = UserRole()
            userRole.id = generate_id()
            userRole.userId = userId
            userRole.roleId = roleId
            self.db.session.add(userRole)

    def online_user_list(self, keywords):
        """
        获取在线用户列表
        """
        tokenKeyPrefix = current_app.config.get("TOKEN_KEY_PREFIX")
        pattern = tokenKeyPrefix + "*"
        redis_client.init_app(current_app)
        keys = list(redis_client.scan_iter(match=pattern))
        userDataList = []
        for key in keys:
            token = key.decode("utf-8").replace(tokenKeyPrefix, "")
            userData = redis_client.get(key)
            if userData is not None:
                userData = json.loads(userData.decode("utf-8"))
                userData["tokenValue"] = token
                if userData["superAdmin"]:
                    # 超级管理员要删除token和tokenValue
                    del userData["token"]
                    del userData["tokenValue"]

                userDataList.append(userData)
        if keywords is not None and len(keywords) > 0:
            userDataList = list(
                filter(lambda x: keywords in x.get("userId"), userDataList)
            )
        tokenDict = {}
        userDataDict = {}
        currentId = LoginUserHolder.get_user_id()
        for userData in userDataList:
            userData["isCurrentUser"] = userData["userId"] == currentId
            userId = userData.get("userId")
            userDataDict[userId] = userData
            tokenList = tokenDict.get(userId)
            if tokenList is None:
                tokenList = [userData]
                tokenDict[userId] = tokenList
            else:
                tokenList.append(userData)
        res = []
        for key, value in tokenDict.items():
            userData = userDataDict.get(key)
            userData["tokenList"] = copy.deepcopy(value)
            time_strings = [t["loginTime"] for t in value]
            datetime_objects = [
                datetime.strptime(t, "%Y-%m-%d %H:%M:%S") for t in time_strings
            ]
            userData["expireTime"] = max(datetime_objects)
            res.append(userData)
        return res

    def logout_by_token_value(self, tokenValues):
        """
        根据token值强制注销
        :param tokenValues:
        :return:
        """
        if tokenValues is None or len(tokenValues) == 0:
            return
        token_strategy = TokenStrategyFactory.get_instance()
        for token in tokenValues:
            token_strategy.remove(token)

    def kickout_by_token_value(self, tokenValues):
        """
        根据token值踢下线
        :param tokenValues:
        :return:
        """
        self.logout_by_token_value(tokenValues)

    def logout_by_login_id(self, loginIds):
        """
        根据登录ID强制注销
        :param loginIds:
        :return:
        """
        if loginIds is None or len(loginIds) == 0:
            return
        token_strategy = TokenStrategyFactory.get_instance()
        tokenKeyPrefix = current_app.config.get("TOKEN_KEY_PREFIX")
        pattern = tokenKeyPrefix + "*"
        redis_client.init_app(current_app)
        keys = list(redis_client.scan_iter(match=pattern))
        for key in keys:
            token = key.decode("utf-8").replace(tokenKeyPrefix, "")
            userData = redis_client.get(key)
            if userData is not None:
                userData = json.loads(userData.decode("utf-8"))
                userId = userData.get("userId")
                if userId in loginIds:
                    token_strategy.remove(token)

    def kickout_by_login_id(self, loginIds):
        """
        根据登录ID踢下线
        :param loginIds:
        :return:
        """
        self.logout_by_login_id(loginIds)

    def get_dept_user_tree(self):
        """
        获取部门用户树
        """
        deptService = ReflectUtil.new_instance(
            "services.dept_service::DeptService", **{"model": Dept}
        )
        deptTree = deptService.tree()
        res = []

        def row_handle_before(data, res):
            """
            前置处理
            """
            vo = {}
            vo["label"] = data.get("name")
            vo["value"] = data.get("id")
            vo["nodeType"] = "1"
            # 部门不可选择
            vo["disabled"] = True
            vo["ext"] = {}
            # 追加部门用户
            vo["children"] = []
            res.append(vo)
            userList = self.list_by_dept_id(data.get("id"))
            for user in userList:
                userVo = {}
                userVo["label"] = user.get("realName")
                userVo["value"] = user.get("id")
                userVo["nodeType"] = "2"
                userVo["disabled"] = False
                userVo["ext"] = {}
                vo["children"].append(userVo)

        recursion_tree(
            tree_data=deptTree,
            row_handle_before=row_handle_before,
            row_handle_after=None,
            res=res,
        )
        return res

    def list_by_dept_id(self, deptId):
        """
        根据部门ID获取用户列表
        :param deptId:
        :return:
        """
        userList = (
            self.db.session.query(User)
            .filter_by(deptId=deptId)
            .filter_by(isDeleted=0)
            .all()
        )
        return User.to_dict_list(userList)

    def find_by_id(self, userId: int) -> dict:
        """
        根据ID获取用户信息
        """
        user: User = self.db.session.query(User).filter_by(id=userId).first()
        if user is None:
            return None
        res = user.to_dict()
        if user.deptId is not None:
            dept: Dept = self.db.session.query(Dept).filter_by(id=user.deptId).first()
            if dept is not None:
                res["deptName"] = dept.name
        if user.postId is not None:
            post: Post = self.db.session.query(Post).filter_by(id=user.postId).first()
            if post is not None:
                res["postName"] = post.name
        return res

    def get_wf_user_by_id(self, userId):
        """
        根据用户ID获取工作流用户信息
        :param userId:
        :return:
        """
        userDict: dict = self.find_by_id(userId)
        if userDict is None:
            return {}
        res = {
            FlowConst.USER_USER_ID: userDict.get("id"),
            FlowConst.USER_REAL_NAME: userDict.get("userName"),
            FlowConst.USER_REAL_NAME: userDict.get("realName"),
            FlowConst.USER_DEPT_ID: userDict.get("deptId"),
            FlowConst.USER_DEPT_NAME: userDict.get("deptName"),
            FlowConst.USER_POST_ID: userDict.get("postId"),
            FlowConst.USER_POST_NAME: userDict.get("postName"),
        }

        return res
